package com.bosssoft.platform.fasttcc.assist;

import com.bosssoft.platform.fasttcc.support.JdbcTransactionInfoDetect;
import com.bosssoft.platform.fasttcc.support.OriginMethodInvoke;
import com.bosssoft.platform.fasttcc.TccTransaction;
import com.bosssoft.platform.fasttcc.Xid;
import com.jfireframework.baseutil.TRACEID;
import com.jfireframework.baseutil.reflect.ReflectUtil;
import com.bosssoft.platform.fasttcc.TccTransactionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

public class TccMethodIntercepter
{
    private              JdbcTransactionInfoDetect jdbcTransactionInfoDetect;
    private              TccTransactionManager     tccTransactionManager;
    private static final Logger                    LOGGER = LoggerFactory.getLogger(TccMethodIntercepter.class);

    public Object onProcessTccMethod(Method method, Object[] params, OriginMethodInvoke invoke)
    {
        boolean                                       create   = false;
        String                                        traceId  = TRACEID.newTraceId();
        JdbcTransactionInfoDetect.TransactionBehavior behavior = jdbcTransactionInfoDetect.detect(method);
        switch (behavior)
        {
            case NONE:
            case PROPAGATION_NEVER:
            case PROPAGATION_NOT_SUPPORTED:
                throw new UnsupportedOperationException("TCC方法必须在本地事务的作用域下");
            case PROPAGATION_REQUIRED:
            {
                TccTransaction tccTransaction = tccTransactionManager.getCurrentTccTransaction();
                if (tccTransaction == null)
                {
                    LOGGER.debug("traceId:{} 要求TCC事务环境，当前还没有TCC事务，准备创建", traceId);
                    tccTransaction = tccTransactionManager.newCoordinatorTccTransaction();
                    create = true;
                }
                Xid localTransactionXid = tccTransaction.getCurrentLocalTransactionXid();
                if (localTransactionXid == null)
                {
                    LOGGER.debug("traceId:{} 当前线程尚未注册本地事务信息，准备注册", traceId);
                    tccTransactionManager.registerLocalTransaction(tccTransaction);
                }
                registerTccInvoke(method, params, tccTransaction);
                break;
            }
            case PROPAGATION_SUPPORTS:
            case PROPAGATION_MANDATORY:
            {
                TccTransaction tccTransaction = tccTransactionManager.getCurrentTccTransaction();
                if (tccTransaction == null || tccTransaction.getCurrentLocalTransactionXid() == null)
                {
                    throw new UnsupportedOperationException("TCC方法必须在本地事务的作用域下");
                }
                registerTccInvoke(method, params, tccTransaction);
                break;
            }
            case PROPAGATION_NESTED:
            {
                throw new UnsupportedOperationException();
            }
            case PROPAGATION_REQUIRES_NEW:
            {
                TccTransaction tccTransaction = tccTransactionManager.getCurrentTccTransaction();
                if (tccTransaction == null)
                {
                    LOGGER.debug("traceId:{} 要求TCC事务环境，当前还没有TCC事务，准备创建", traceId);
                    tccTransaction = tccTransactionManager.newCoordinatorTccTransaction();
                    create = true;
                }
                LOGGER.debug("traceId:{} 注册新的本地事务信息", traceId);
                tccTransactionManager.registerLocalTransaction(tccTransaction);
                registerTccInvoke(method, params, tccTransaction);
                break;
            }
        }
        LOGGER.debug("traceId:{} 准备执行业务方法", traceId);
        try
        {
            Object result = invoke.invoke();
            LOGGER.debug("traceId:{} 业务方法执行成功，标记当前本地事务提交成功", traceId);
            tccTransactionManager.getCurrentTccTransaction().commitCurrentLocalTransaction();
            return result;
        }
        catch (Throwable e)
        {
            LOGGER.debug("traceId:{} 业务方法执行异常，标记当前本地事务回滚", traceId);
            tccTransactionManager.getCurrentTccTransaction().rollbackCurrentLocalTransaction();
            ReflectUtil.throwException(e);
            return null;
        }
        finally
        {
            TccTransaction tccTransaction = tccTransactionManager.getCurrentTccTransaction();
            if (tccTransaction.role() == TccTransaction.COORDINATOR && tccTransaction.getCurrentLocalTransactionXid() == null)
            {
                if (tccTransaction.isFirstLocalTransactionCommited())
                {
                    LOGGER.debug("traceId:{} TCC事务:{}已经完成尝试阶段，最外围本地事务提交成功，将TCC事务标记为提交", traceId, tccTransaction.getXid());
                    tccTransaction.markForCommit();
                }
                else
                {
                    LOGGER.debug("traceId:{} TCC事务:{}已经完成尝试阶段，最外围本地事务回滚完成，将TCC事务标记为回滚", traceId, tccTransaction.getXid());
                    tccTransaction.markForRollback();
                }
                tccTransaction.processCompleteStage();
            }
            if (create)
            {
                tccTransactionManager.deAssociateTccTransaction();
            }
        }
    }

    private void registerTccInvoke(Method method, Object[] params, TccTransaction tccTransaction)
    {
        tccTransactionManager.registerTccInvoke(method, params, tccTransaction);
    }

    public void setJdbcTransactionInfoDetect(JdbcTransactionInfoDetect jdbcTransactionInfoDetect)
    {
        this.jdbcTransactionInfoDetect = jdbcTransactionInfoDetect;
    }

    public void setTccTransactionManager(TccTransactionManager tccTransactionManager)
    {
        this.tccTransactionManager = tccTransactionManager;
    }
}
